import matplotlib
from scipy.io import wavfile
from scipy.fftpack import fftfreq
matplotlib.use('WXAgg')
from fractions import Fraction
from numpy import zeros, concatenate, fft
import numpy
from math import *

from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.backends.backend_wx import NavigationToolbar2Wx
from matplotlib.figure import Figure

import wx


class Effects(wx.Panel):

    def __init__(self, parent, model):
        wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)
        self.parent = parent
        self.model = model
        self.audio = ""

        sizer = wx.BoxSizer(wx.HORIZONTAL)
        chart = wx.BoxSizer(wx.VERTICAL)
        options = wx.BoxSizer(wx.VERTICAL)

        self.option = wx.ComboBox(self, 1, pos=(50, 170), size=(150, -1), choices=[], style=wx.CB_READONLY)
	self.option.Bind(wx.EVT_COMBOBOX, self.updateFigure)

        chart.Add(self.option, 0, wx.ALL, 5)
        
        self.figure = Figure()
        self.axes = self.figure.add_subplot(111)
        self.canvas = FigureCanvas(self, -1, self.figure)

        chart.Add(self.canvas)
        sizer.Add(chart, 0, wx.ALL, 5)

        delay = wx.StaticBox(self, wx.ID_ANY, "Delay")
        delayLengthSizer = wx.BoxSizer(wx.HORIZONTAL)
        delayLengthLabel = wx.StaticText(self, label="Length in s")
        self.delayLength = wx.TextCtrl(self, wx.ID_ANY)
        delayLengthSizer.Add(delayLengthLabel, 0, wx.ALL, 5)
        delayLengthSizer.Add(self.delayLength, 0, wx.ALL, 5)
        applyDelay = wx.Button(self, wx.ID_ANY, "Apply Delay")
	applyDelay.Bind(wx.EVT_BUTTON, self.delay)

        echo = wx.StaticBox(self, wx.ID_ANY, "Echo")
        echoDelaySizer = wx.BoxSizer(wx.HORIZONTAL)
        echoDelayLabel = wx.StaticText(self, label="Delay")
        self.echoDelay = wx.TextCtrl(self, wx.ID_ANY)
        echoDelaySizer.Add(echoDelayLabel, 0, wx.ALL, 5)
        echoDelaySizer.Add(self.echoDelay, 0, wx.ALL, 5)
        echoLengthSizer = wx.BoxSizer(wx.HORIZONTAL)
        echoLengthLabel = wx.StaticText(self, label="Length")
        self.echoLength = wx.TextCtrl(self, wx.ID_ANY)
        echoLengthSizer.Add(echoLengthLabel, 0, wx.ALL, 5)
        echoLengthSizer.Add(self.echoLength, 0, wx.ALL, 5)
        echoGainSizer = wx.BoxSizer(wx.HORIZONTAL)
        echoGainLabel = wx.StaticText(self, label="Gain")
        self.echoGain = wx.TextCtrl(self, wx.ID_ANY)
        echoGainSizer.Add(echoGainLabel, 0, wx.ALL, 5)
        echoGainSizer.Add(self.echoGain, 0, wx.ALL, 5)
        applyEcho = wx.Button(self, wx.ID_ANY, "Apply Echo")
	applyEcho.Bind(wx.EVT_BUTTON, self.echo)

        pitch = wx.StaticBox(self, wx.ID_ANY, "Pitch")
        pitchLengthSizer = wx.BoxSizer(wx.HORIZONTAL)
        pitchLengthLabel = wx.StaticText(self, label="Length")
        self.pitchLength = wx.TextCtrl(self, wx.ID_ANY, "")
        pitchLengthSizer.Add(pitchLengthLabel, 0, wx.ALL, 5)
        pitchLengthSizer.Add(self.pitchLength, 0, wx.ALL, 5)
        applyPitch = wx.Button(self, wx.ID_ANY, "Apply Pitch")
	applyPitch.Bind(wx.EVT_BUTTON, self.pitchshift)

        decimator = wx.StaticBox(self, wx.ID_ANY, "Decimator")
        decimatorRateSizer = wx.BoxSizer(wx.HORIZONTAL)
        decimatorRateLabel = wx.StaticText(self, label="Rate")
        self.decimatorRate = wx.TextCtrl(self, wx.ID_ANY, "")
        decimatorRateSizer.Add(decimatorRateLabel, 0, wx.ALL, 5)
        decimatorRateSizer.Add(self.decimatorRate, 0, wx.ALL, 5)
        applyDecimator = wx.Button(self, wx.ID_ANY, "Apply Decimator")
	applyDecimator.Bind(wx.EVT_BUTTON, self.decimator)

        bitcrusher = wx.StaticBox(self, wx.ID_ANY, "Bitcrusher")
        bitcrusherDivisorSizer = wx.BoxSizer(wx.HORIZONTAL)
        bitcrusherDivisorLabel = wx.StaticText(self, label="Bits")
        self.bitcrusherDivisor = wx.TextCtrl(self, wx.ID_ANY, "")
        bitcrusherDivisorSizer.Add(bitcrusherDivisorLabel, 0, wx.ALL, 5)
        bitcrusherDivisorSizer.Add(self.bitcrusherDivisor, 0, wx.ALL, 5)
        bitcrusherRateSizer = wx.BoxSizer(wx.HORIZONTAL)
        bitcrusherRateLabel = wx.StaticText(self, label="Rate")
        self.bitcrusherRate = wx.TextCtrl(self, wx.ID_ANY, "")
        bitcrusherRateSizer.Add(bitcrusherRateLabel, 0, wx.ALL, 5)
        bitcrusherRateSizer.Add(self.bitcrusherRate, 0, wx.ALL, 5)
        applyBitcrusher = wx.Button(self, wx.ID_ANY, "Apply Bitcrusher")
	applyBitcrusher.Bind(wx.EVT_BUTTON, self.bitcrusher)

        tsps = wx.StaticBox(self, wx.ID_ANY, "Time Stretch Pitch Shift")
        tspsLengthSizer = wx.BoxSizer(wx.HORIZONTAL)
        tspsLengthLabel = wx.StaticText(self, label="Length")
        self.tspsLength = wx.TextCtrl(self, wx.ID_ANY, "")
        tspsLengthSizer.Add(tspsLengthLabel, 0, wx.ALL, 5)
        tspsLengthSizer.Add(self.tspsLength, 0, wx.ALL, 5)
        applyTsps = wx.Button(self, wx.ID_ANY, "Apply Time Stretch Pitch Shift")
        applyTsps.Bind(wx.EVT_BUTTON, self.tsps)

        delayBox = wx.StaticBoxSizer(delay, wx.VERTICAL)
        delayBox.SetMinSize((300, 150))
        delayBox.Add(delayLengthSizer, 0, wx.ALL, 5)
        delayBox.Add(applyDelay, 0, wx.ALL, 5)

        echoBox = wx.StaticBoxSizer(echo, wx.VERTICAL)
        echoBox.SetMinSize((300, 150))
        echoBox.Add(echoDelaySizer, 0, wx.ALL, 5)
        echoBox.Add(echoLengthSizer, 0, wx.ALL, 5)
        echoBox.Add(echoGainSizer, 0, wx.ALL, 5)
        echoBox.Add(applyEcho, 0, wx.ALL, 5)

        pitchBox = wx.StaticBoxSizer(pitch, wx.VERTICAL)
        pitchBox.SetMinSize((300, 150))
        pitchBox.Add(pitchLengthSizer, 0, wx.ALL, 5)
        pitchBox.Add(applyPitch, 0, wx.ALL, 5)

        decimatorBox = wx.StaticBoxSizer(decimator, wx.VERTICAL)
        decimatorBox.SetMinSize((300, 150))
        decimatorBox.Add(decimatorRateSizer, 0, wx.ALL, 5)
        decimatorBox.Add(applyDecimator, 0, wx.ALL, 5)

        bitcrusherBox = wx.StaticBoxSizer(bitcrusher, wx.VERTICAL)
        bitcrusherBox.SetMinSize((300, 150))
        bitcrusherBox.Add(bitcrusherDivisorSizer, 0, wx.ALL, 5)
        bitcrusherBox.Add(bitcrusherRateSizer, 0, wx.ALL, 5)
        bitcrusherBox.Add(applyBitcrusher, 0, wx.ALL, 5)

        tspsBox = wx.StaticBoxSizer(tsps, wx.VERTICAL)
        tspsBox.SetMinSize((300, 150))
        tspsBox.Add(tspsLengthSizer, 0, wx.ALL, 5)
        tspsBox.Add(applyTsps, 0, wx.ALL, 5)

        options.Add(delayBox, 0, wx.ALL, 5)
        options.Add(echoBox, 0, wx.ALL, 5)
        options.Add(pitchBox, 0, wx.ALL, 5)

        options2 = wx.BoxSizer(wx.VERTICAL)
        options2.Add(decimatorBox, 0, wx.ALL, 5)
        options2.Add(bitcrusherBox, 0, wx.ALL, 5)
        options2.Add(tspsBox, 0, wx.ALL, 5)

        self.save = wx.Button(self, 3, "Save")
        wx.EVT_BUTTON(self, 3, self.OnSave)
        options2.Add(self.save, 0, wx.ALL, 5)

        sizer.Add(options, 0, wx.ALL, 5)
        sizer.Add(options2, 0, wx.ALL, 5)

        self.SetSizer(sizer)

    def decimator(self, event):
        audio = self.audio[1]
        rate = float(self.decimatorRate.GetValue())
        count = 0
        location = 0
        bits = 16
        multiplier = 1<<(bits-1)
        output = zeros(len(audio))
        for sample in audio:
            count = count + rate
            if count >= 1:
                count = count - 1
                output[location] = float(long(sample*multiplier)/float(multiplier))
                location = location + 1
        self.audio = (self.audio[0], output)
        self.axes.clear()
        x = numpy.array(range(0, len(self.audio[1])))/44100.0
        self.axes.plot(x, self.audio[1]/(2.0**15))
        self.figure.canvas.draw()


    def bitcrusher(self, event):
        audio = self.audio[1]
        bits = int(self.bitcrusherDivisor.GetValue())
        bitrate = int(self.bitcrusherRate.GetValue())
        rangeSize = pow(2, bits) - 1
        step = 44100/ bitrate
        step = 2**bits
        location = 0
        output = zeros(len(audio))
        sampleLocation = 0
        current = 0
        for sample in audio:
            sample = floor(float(sample*step) + 0.5)/step
            if sampleLocation < step:
                output[location] = current
                location = location + 1
                sampleLocation = sampleLocation + 1
            else:
                current = sample
                output[location] = current
                location = location + 1
                sampleLocation = 0
        self.audio = (self.audio[0], output)
        self.axes.clear()
        x = numpy.array(range(0, len(self.audio[1])))/44100.0
        self.axes.plot(x, output/(2.0**15))
        self.figure.canvas.draw()
                

    def delay(self, event):
        length = float(self.delayLength.GetValue())
        delay = [0] * int(length * self.audio[0])
        item = concatenate((delay, self.audio[1]))
        self.audio = (self.audio[0], item)
        self.axes.clear()
        x = numpy.array(range(0, len(self.audio[1])))/44100.0
        self.axes.plot(x, self.audio[1]/(2.0**15))
        self.figure.canvas.draw()

    def echo(self, event):
        delay = int(float(self.echoDelay.GetValue()) * self.audio[0])
        length = int(self.echoLength.GetValue()) * self.audio[0]
        gain = float(self.echoGain.GetValue())
        item = self.echoRecurse(self.audio[1], delay, gain, length)
        self.audio = (self.audio[0], item)
        self.axes.clear()
        x = numpy.array(range(0, len(self.audio[1])))/44100.0
        self.axes.plot(x, self.audio[1]/(2.0**15))
        self.figure.canvas.draw()
        
    def echoRecurse(self, audio, delay, gain, length):
        #print delay
        #print length
        #print gain
        #print len(audio)
        count = 0
        output = []
        echoOutput = zeros(delay)
        output = zeros(len(audio)+1)
        for sample in audio:
            count = count + 1
            length = length - 1
            if length == 0:
                return output
            if delay == count:
                echoOutput = self.echoRecurse(audio, delay, gain, length+1) * gain
                #print echoOutput
            output[count] = sample + echoOutput[count-delay]
            #print output
        output = concatenate((output, echoOutput[count-delay:]))
        return output

    def pitchshift(self, event):
        pitch = int(self.pitchLength.GetValue())
        if pitch < 0:
            self.audio = (self.audio[0], self.decreasePitch(int(abs(pitch)), self.audio[1]))
        elif pitch > 0:
            self.audio = (self.audio[0], self.increasePitch(int(abs(pitch)), self.audio[1]))
        self.axes.clear()
        x = numpy.array(range(0, len(self.audio[1])))/44100.0
	self.axes.plot(x, self.audio[1]/(2.0**15))
        self.figure.canvas.draw()

    def increasePitch(self, pitch, audio):
        count = 0
        output = zeros((len(audio)/pitch)+1)
        loc = 0
        for sample in audio:
            count = count + 1
            if count >= pitch:
                output[loc] = sample
                loc = loc + 1
                count = 0
        return output

    def decreasePitch(self, pitch, audio):
        output = zeros(len(audio)*pitch)
        count = 0
        for sample in audio:
                output[count:count+pitch] = [sample] * pitch
                count = count + pitch
            #print output
        return output

    def tsps(self, event):
        return

    def updateFigure(self, event):
        filename = self.option.GetValue()
        location = self.model.getFile(filename)
        self.audio = self.model.getAudio(location)
	self.axes.clear()
	x = numpy.array(range(0, len(self.audio[1])))/44100.0
        self.axes.plot(x, self.audio[1]/(2.0**15))
        self.figure.canvas.draw()


    def OnSave(self, event):
        dlg = wx.FileDialog(
            self, message="Choose a file",
            defaultFile="",
            style=wx.SAVE | wx.CHANGE_DIR
            )
        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            wavfile.write(path, self.audio[0], self.audio[1])
        self.axes.clear()
        x = numpy.array(range(0, len(audio[1])))/44100.0
        self.plot = self.axes.plot(x, self.audio[1])
        self.figure.canvas.draw()

    def update(self):
        self.option.Clear()
        filenames = self.model.getFilenames()
        for filename in filenames:
            self.option.Append(filename.keys()[0])
